//
// Created by twetec on 25-8-17.
//

#include "handle_mongodb_probe_task.h"

#include <boost/json.hpp>
#include <bson/bson.h>
#include <mongoc/mongoc.h>

#include <bs_thread_pool/BS_thread_pool.hpp>

#include <network_prober/utils/constants.h>

using namespace std::chrono_literals;

namespace network_prober::probe
{
// 线程池
static BS::thread_pool s_iopool{1000};

// // libmongoc初始化程序，只执行一次
// static std::once_flag s_mongoClientInitOnceFlag;
// // mongoc客户端初始化锁
// static pthread_mutex_t s_mongoClientInitMutex = PTHREAD_MUTEX_INITIALIZER;

// MongoDBProbeIPv4::MongoDBProbeIPv4(
//     std::string_view interface,
//     std::string_view target,
//     std::uint32_t port,
//     std::string_view userName,
//     std::string_view password,
//     std::string_view databaseName,
//     std::string_view queryStatement
// )
// : m_interface(interface), m_target(target), m_port(port), m_userName(userName),
// m_password(password), m_databaseName(databaseName), m_queryStatement(queryStatement)
// {
//     // 初始化libmongoc库，在应用程序启动时，只调用一次
//     std::call_once(utils::g_mongoClientInitOnceFlag, [] {
//         // 初始化正常
//         mongoc_init();
//     });
// }
//
// void MongoDBProbeIPv4::setOnProbedCallback(std::function<void(std::int64_t)> callback)
// {
//     m_onProbedCallback = std::move(callback);
// }
//
// void MongoDBProbeIPv4::probe()
// {
//     // 建立连接时，开始计时
//     m_probeStartTime = std::chrono::steady_clock::now();
//
//     // 指向当前对象的shared_ptr
//     auto self{shared_from_this()};
//
//     // 发起探测
//     m_onProbedCallbackFuture = std::async(std::launch::async, [this, self] {
//         // 加载mongoc驱动
//         // mongoc_init();
//
//         // 创建MongoDB客户端
//         std::stringstream ss;
//         ss << "mongodb://" << m_target << ":" << m_port;
//
//         pthread_mutex_lock(&utils::g_mongoClientInitMutex);
//         mongoc_client_t *client = mongoc_client_new(ss.str().c_str());
//         pthread_mutex_unlock(&utils::g_mongoClientInitMutex);
//
//         // 如果连接失败
//         if (!client) {
//             // 报错信息
//             SPDLOG_INFO("MongoDB的client对象创建失败..");
//             // 清除mongoc驱动
//             // mongoc_cleanup();
//             // 回调结果,连接失败，延迟为-1
//             m_onProbedCallback(-1);
//             // 结束执行
//             return;
//         }
//
//         // 设定超时时间
//         mongoc_client_set_sockettimeoutms(client, 13 * 1000);
//
//         // 准备ping命令
//         bson_t *ping = BCON_NEW ("ping", BCON_INT32 (1));
//         bson_t reply = BSON_INITIALIZER;
//         bson_error_t error;
//
//         // 发送ping命令，如果失败
//         if (!mongoc_client_command_with_opts(client, "admin", ping, NULL, NULL, &reply, &error)) {
//             // 报错信息
//             SPDLOG_INFO("MongoDB的ping命令执行失败: {}", error.message);
//             // 回收资源
//             bson_destroy(&reply);
//             bson_destroy(ping);
//             mongoc_client_destroy(client);
//             // 清除mongoc驱动
//             // mongoc_cleanup();
//             // 回调结果,连接失败，延迟为-1
//             m_onProbedCallback(-1);
//             // 结束执行
//             return;
//         }
//
//         // 回收资源
//         bson_destroy (&reply);
//         bson_destroy (ping);
//         mongoc_client_destroy (client);
//
//         // 清除mongoc驱动
//         // mongoc_cleanup();
//
//         // 回调结果
//         m_onProbedCallback(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_probeStartTime).count());
//     });
// }
//
// MongoDBProbeIPv4::~MongoDBProbeIPv4()
// {
//
// }

HandleMongoDBProbeIPv4Awaiter::HandleMongoDBProbeIPv4Awaiter(const database::ProbeTaskModel &probeTask)
: m_interface(probeTask.interface), m_target(probeTask.address), m_port(probeTask.port), m_userName(probeTask.userName),
m_password(probeTask.password), m_databaseName(probeTask.databaseName), m_queryStatement(probeTask.queryStatement)
{
    // 初始化libmongoc库，在应用程序启动时，只调用一次
    std::call_once(utils::g_mongoClientInitOnceFlag, [] {
        // 初始化正常
        mongoc_init();
    });
}

bool HandleMongoDBProbeIPv4Awaiter::await_ready()
{
    return false;
}

void HandleMongoDBProbeIPv4Awaiter::await_suspend(std::coroutine_handle<> handle)
{
    s_iopool.submit_task([this, handle] {
        // 线程锁
        std::mutex mtx;
        std::condition_variable cv;

        // 请求计时 - 开始时间
        std::chrono::time_point<std::chrono::steady_clock> probeStartTime = std::chrono::steady_clock::now();

        // 发起探测
        std::future future = std::async(std::launch::async, [this, &probeStartTime, &mtx, &cv] {
            // 加载mongoc驱动
            // mongoc_init();

            // 创建MongoDB客户端
            std::stringstream ss;
            ss << "mongodb://" << m_target << ":" << m_port;

            pthread_mutex_lock(&utils::g_mongoClientInitMutex);
            mongoc_client_t *client = mongoc_client_new(ss.str().c_str());
            pthread_mutex_unlock(&utils::g_mongoClientInitMutex);

            // 如果连接失败
            if (!client) {
                // 报错信息
                SPDLOG_INFO("MongoDB的client对象创建失败..");
                // 清除mongoc驱动
                // mongoc_cleanup();
                // 回调结果,连接失败，延迟为-1
                // 加锁
                std::unique_lock<std::mutex> lock(mtx);
                // 保存延迟
                m_delay = -1;
                // 通知延迟保存成功
                cv.notify_one();
                // 结束执行
                return;
            }

            // 设定超时时间
            mongoc_client_set_sockettimeoutms(client, 6 * 1000);

            // 准备ping命令
            bson_t *ping = BCON_NEW ("ping", BCON_INT32 (1));
            bson_t reply = BSON_INITIALIZER;
            bson_error_t error;

            // 发送ping命令，如果失败
            if (!mongoc_client_command_with_opts(client, "admin", ping, NULL, NULL, &reply, &error)) {
                // 报错信息
                SPDLOG_INFO("MongoDB的ping命令执行失败: {}", error.message);
                // 回收资源
                bson_destroy(&reply);
                bson_destroy(ping);
                mongoc_client_destroy(client);
                // 清除mongoc驱动
                // mongoc_cleanup();
                // 回调结果,连接失败，延迟为-1
                // 加锁
                std::unique_lock<std::mutex> lock(mtx);
                // 保存延迟
                m_delay = -1;
                // 通知延迟保存成功
                cv.notify_one();
                // 结束执行
                return;
            }

            // 回收资源
            bson_destroy (&reply);
            bson_destroy (ping);
            mongoc_client_destroy (client);

            // 清除mongoc驱动
            // mongoc_cleanup();

            // 加锁
            std::unique_lock<std::mutex> lock(mtx);
            // 保存延迟
            m_delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - probeStartTime).count();
            // 通知延迟保存成功
            cv.notify_one();
        });

        // 等待响应（12s）
        std::unique_lock<std::mutex> lock(mtx);
        std::cv_status cvStatus = cv.wait_for(lock, 8s);

        // 如果等待超时,则将延迟置为-1
        if (cvStatus == std::cv_status::timeout) m_delay = -1;

        // 恢复暂停的协程
        handle.resume();
    });
}

std::int64_t HandleMongoDBProbeIPv4Awaiter::await_resume()
{
    return m_delay;
}


// MongoDBProbeIPv6::MongoDBProbeIPv6(
//     std::string_view interface,
//     std::string_view target,
//     std::uint32_t port,
//     std::string_view userName,
//     std::string_view password,
//     std::string_view databaseName,
//     std::string_view queryStatement
// )
// : m_interface(interface), m_target(target), m_port(port), m_userName(userName),
// m_password(password), m_databaseName(databaseName), m_queryStatement(queryStatement)
// {
//     // 初始化libmongoc库，在应用程序启动时，只调用一次
//     std::call_once(utils::g_mongoClientInitOnceFlag, [] {
//         // 初始化正常
//         mongoc_init();
//     });
// }
//
// void MongoDBProbeIPv6::setOnProbedCallback(std::function<void(std::int64_t)> callback)
// {
//     m_onProbedCallback = std::move(callback);
// }
//
// void MongoDBProbeIPv6::probe()
// {
//     // 建立连接时，开始计时
//     m_probeStartTime = std::chrono::steady_clock::now();
//
//     // 指向当前对象的shared_ptr
//     auto self{shared_from_this()};
//
//     // 发起探测
//     m_onProbedCallbackFuture = std::async(std::launch::async, [this, self] {
//         // 加载mongoc驱动
//         // mongoc_init();
//
//         // 创建MongoDB客户端
//         std::stringstream ss;
//         ss << "mongodb://[" << m_target << "]:" << m_port;
//
//         pthread_mutex_lock(&utils::g_mongoClientInitMutex);
//         mongoc_client_t *client = mongoc_client_new(ss.str().c_str());
//         pthread_mutex_unlock(&utils::g_mongoClientInitMutex);
//
//         // 如果连接失败
//         if (!client) {
//             // 报错信息
//             SPDLOG_INFO("MongoDB的client对象创建失败..");
//             // 清除mongoc驱动
//             // mongoc_cleanup();
//             // 回调结果,连接失败，延迟为-1
//             m_onProbedCallback(-1);
//             // 结束执行
//             return;
//         }
//
//         // 设定超时时间
//         mongoc_client_set_sockettimeoutms(client, 13 * 1000);
//
//         // 准备ping命令
//         bson_t *ping = BCON_NEW ("ping", BCON_INT32 (1));
//         bson_t reply = BSON_INITIALIZER;
//         bson_error_t error;
//
//         // 发送ping命令，如果失败
//         if (!mongoc_client_command_with_opts(client, "admin", ping, NULL, NULL, &reply, &error)) {
//             // 报错信息
//             SPDLOG_INFO("MongoDB的ping命令执行失败: {}", error.message);
//             // 回收资源
//             bson_destroy(&reply);
//             bson_destroy(ping);
//             mongoc_client_destroy(client);
//             // 清除mongoc驱动
//             // mongoc_cleanup();
//             // 回调结果,连接失败，延迟为-1
//             m_onProbedCallback(-1);
//             // 结束执行
//             return;
//         }
//
//         // 回收资源
//         bson_destroy(&reply);
//         bson_destroy(ping);
//         mongoc_client_destroy(client);
//         // 清除mongoc驱动
//         // mongoc_cleanup();
//
//         // 回调结果
//         m_onProbedCallback(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_probeStartTime).count());
//     });
// }
//
// MongoDBProbeIPv6::~MongoDBProbeIPv6()
// {
// }

HandleMongoDBProbeIPv6Awaiter::HandleMongoDBProbeIPv6Awaiter(const database::ProbeTaskModel &probeTask)
: m_interface(probeTask.interface), m_target(probeTask.address), m_port(probeTask.port), m_userName(probeTask.userName),
m_password(probeTask.password), m_databaseName(probeTask.databaseName), m_queryStatement(probeTask.queryStatement)
{
    // 初始化libmongoc库，在应用程序启动时，只调用一次
    std::call_once(utils::g_mongoClientInitOnceFlag, [] {
        // 初始化正常
        mongoc_init();
    });
}

bool HandleMongoDBProbeIPv6Awaiter::await_ready()
{
    return false;
}

void HandleMongoDBProbeIPv6Awaiter::await_suspend(std::coroutine_handle<> handle)
{
    s_iopool.submit_task([this, handle] {
        // 线程锁
        std::mutex mtx;
        std::condition_variable cv;

        // 请求计时 - 开始时间
        std::chrono::time_point<std::chrono::steady_clock> probeStartTime = std::chrono::steady_clock::now();

        // 发起探测
        std::future future = std::async(std::launch::async, [this, &probeStartTime, &mtx, &cv] {
            // 加载mongoc驱动
            // mongoc_init();

            // 创建MongoDB客户端
            std::stringstream ss;
            ss << "mongodb://[" << m_target << "]:" << m_port;

            pthread_mutex_lock(&utils::g_mongoClientInitMutex);
            mongoc_client_t *client = mongoc_client_new(ss.str().c_str());
            pthread_mutex_unlock(&utils::g_mongoClientInitMutex);

            // 如果连接失败
            if (!client) {
                // 报错信息
                SPDLOG_INFO("MongoDB的client对象创建失败..");
                // 清除mongoc驱动
                // mongoc_cleanup();
                // 回调结果,连接失败，延迟为-1
                // 加锁
                std::unique_lock<std::mutex> lock(mtx);
                // 保存延迟
                m_delay = -1;
                // 通知延迟保存成功
                cv.notify_one();
                // 结束执行
                return;
            }

            // 设定超时时间
            mongoc_client_set_sockettimeoutms(client, 6 * 1000);

            // 准备ping命令
            bson_t *ping = BCON_NEW ("ping", BCON_INT32 (1));
            bson_t reply = BSON_INITIALIZER;
            bson_error_t error;

            // 发送ping命令，如果失败
            if (!mongoc_client_command_with_opts(client, "admin", ping, NULL, NULL, &reply, &error)) {
                // 报错信息
                SPDLOG_INFO("MongoDB的ping命令执行失败: {}", error.message);
                // 回收资源
                bson_destroy(&reply);
                bson_destroy(ping);
                mongoc_client_destroy(client);
                // 清除mongoc驱动
                // mongoc_cleanup();
                // 回调结果,连接失败，延迟为-1
                // 加锁
                std::unique_lock<std::mutex> lock(mtx);
                // 保存延迟
                m_delay = -1;
                // 通知延迟保存成功
                cv.notify_one();
                // 结束执行
                return;
            }

            // 回收资源
            bson_destroy(&reply);
            bson_destroy(ping);
            mongoc_client_destroy(client);
            // 清除mongoc驱动
            // mongoc_cleanup();

            // 加锁
            std::unique_lock<std::mutex> lock(mtx);
            // 保存延迟
            m_delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - probeStartTime).count();
            // 通知延迟保存成功
            cv.notify_one();
        });

        // 等待响应（12s）
        std::unique_lock<std::mutex> lock(mtx);
        std::cv_status cvStatus = cv.wait_for(lock, 8s);

        // 如果等待超时,则将延迟置为-1
        if (cvStatus == std::cv_status::timeout) m_delay = -1;

        // 恢复暂停的协程
        handle.resume();
    });
}

std::int64_t HandleMongoDBProbeIPv6Awaiter::await_resume()
{
    return m_delay;
}

}